using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;
using Mogre;



namespace com.petiteappli.xenocide
{
    

    public class MobileObject:IDestroyable,ITimerUpdatable,IDisposable,IMyMouseListener,ISelectable
    {
        #region Protected Fields
        protected Entity _entity;
        protected SceneNode _node, mRadarNode, mNodeSelect, mNodeEntity;
        protected String _name;
        protected World _world;
        protected bool mSelected;
        protected const float _CONTACT_DISTANCE = 20.0f;
        protected float mNodeSelectScale, mNodeSelectScaleStep, mNodeSelectMaxScale, mNodeSelectMinScale;
        protected int mSize;

        protected int mHP;
        protected int mMaxHP;
        protected int mArmor;

        protected bool mDead;

        
        #endregion

        
       
        #region Properties
        public Entity entity
        {
            get { return _entity; }
            set { _entity = value; }
        }

        public String Name
        {
            get { return _name; }
            set
            {
                _name = value;
           }
        }

        public World ContainingWorld
        {
            get { return _world; }

        }

        public SceneNode node
        {
            get { return _node; }
            set { _node = value; }
        }

        public virtual Vector3 Position
        {
            get { return _node.Position; }
            set
            {
                _node.Position = value;
                
            }
        }
        public virtual Quaternion Orientation
        {
            get { return _node.Orientation; }
            set
            {
                _node.Orientation = value;
               
            }
        }

        public bool IsSelected()
        {
            return mSelected;
        }
        public void SetSelected(bool value)
        {
            mSelected = value;
            if (value)
            {
                if (_world.MainWindow.mBBS.ParentSceneNode != null)
                {
                    _world.MainWindow.log("***old : " + _world.MainWindow.mBBS.WorldPosition.ToString());

                    _world.MainWindow.mBBS.ParentSceneNode.DetachObject("selection set");
                    _world.MainWindow.log("detaching mBBS");
                }

                mNodeSelect.AttachObject(_world.MainWindow.mBBS);
                _world.MainWindow.log(_world.MainWindow.mBBS.WorldPosition.ToString());
                _world.MainWindow.log(_world.MainWindow.mBBS.GetBillboard(0).Position.ToString());
                //_world.MainWindow.mBBS.CreateBillboard(Vector3.UNIT_Y * 10.0f, ColourValue.Red);
                //mBB.SetTexcoordRect(0.0f, 0.0f, 1.0f, 1.0f);
            }
            else
            {
                _world.MainWindow.log("***old : " + _world.MainWindow.mBBS.WorldPosition.ToString());
                mNodeSelect.DetachObject(_world.MainWindow.mBBS);
            }
        }
        

        #endregion

        public void SetHPMax(int iMHP)
        {
            mMaxHP = iMHP;
        }

        public int RemainingHP()
        {
            return mHP;
        }

        

        public float DamageLevel()
        {
            return (float)mHP / mMaxHP;
        }

        public void Inflict(int iDamage)
        {
            mHP -= iDamage;
        }
        public bool Repair(int iRepair)
        {
            mHP += iRepair;
            if (mHP >= mMaxHP)
            {
                mHP = mMaxHP;
                return true;
            }
            return false;
        }

        public float FlatDistanceTo(MobileObject iMO)
        {
            Vector2 pos = new Vector2(_node.Position.x-iMO.Position.x, _node.Position.z-iMO.Position.z);
            return pos.Length;

        }

        public int Size
        {
            get { return mSize; }
            set {mSize=value;}
        }

        #region Constructor
        public MobileObject(World iWorld, String iName, Vector3 iPos, Quaternion iOrient)
        {
            _world = iWorld;
            _name = iName;
            mDead=false;

            _node = _world.MainWindow.SceneMgr.RootSceneNode.CreateChildSceneNode();
            mSize = 20;
            try
            {

                _entity = _world.MainWindow.SceneMgr.CreateEntity(iName, "sphere.mesh");

            }
            catch (Exception e)
            {
                iWorld.MainWindow.logWarning("Error creating entity for " + iName);
                iWorld.MainWindow.log(e.Message);

            }
            
            mNodeEntity = _node.CreateChildSceneNode();
            mNodeEntity.AttachObject(_entity);
            mNodeEntity.SetScale(1f, 1f, 1f);

            mNodeSelect = _node.CreateChildSceneNode(Vector3.UNIT_Y * 20.0f);
            mNodeSelect.InheritScale = false;

            mNodeSelectMaxScale = 1.0f;
            mNodeSelectMinScale = 0.3f;
            mNodeSelect.Scale(Vector3.UNIT_SCALE * mNodeSelectMinScale);
            mNodeSelectScale = mNodeSelectMinScale;
            mNodeSelectScaleStep = mNodeSelectMaxScale / 5.0f;
            _node.Position = iPos;
            _node.Orientation = iOrient;

            

        }
        #endregion

        public virtual void Dispose()
        {
            mDead=true;
            _world.MainWindow.SceneMgr.DestroyEntity(_entity);
            _world.MainWindow.SceneMgr.DestroySceneNode(_node.Name);
            

        }
        public virtual bool timerUpdate(float iTimeInterval)
        {

            if (mSelected)
            {
                mNodeSelectScale += mNodeSelectScaleStep;
                if ((mNodeSelectScale >= mNodeSelectMaxScale) || (mNodeSelectScale <= mNodeSelectMinScale)) mNodeSelectScaleStep = -mNodeSelectScaleStep;
                mNodeSelect.SetScale(Vector3.UNIT_SCALE * mNodeSelectScale);
            }
            return true;
        }

        public virtual bool DoMouseDown(Vector3 pos, MOIS.MouseButtonID button, bool iShift, bool iCtrl, bool iAlt, MyOgreWindow.QueryResult iQr, ISelectable iMO)
        {
            return true;
        }
        public virtual bool DoMouseUp(Vector3 pos, MOIS.MouseButtonID button, bool iShift, bool iCtrl, bool iAlt, MyOgreWindow.QueryResult iQr, ISelectable iMO)
        {
            return true;
        }

		

    }
	public class MobileActor:MobileObject
    {
        
		#region Public Fields
		public enum Behaviour {FollowPath,Idle};
		#endregion
        

		#region Protected Fields
		public struct T_WayPoint
		{
			public Vector3 pos;
			Entity entity;
			SceneNode sceneNode;
			World _world;
			
			public T_WayPoint(World iWorld, Vector3 iPos,String iName)
			{
				_world=iWorld;
				sceneNode=_world.MainWindow.SceneMgr.RootSceneNode.CreateChildSceneNode();
				entity=_world.MainWindow.SceneMgr.CreateEntity(iName, "sphere.mesh");
                sceneNode.Scale(0.1f, 0.1f, 0.1f);
                sceneNode.Position = iPos;
                sceneNode.AttachObject(entity);
                pos = iPos;
			}
            public void Dispose()
            {
                sceneNode.DetachAllObjects();
                
                _world.MainWindow.SceneMgr.DestroySceneNode(sceneNode.Name);
                sceneNode = null;
                if (entity!=null) _world.MainWindow.SceneMgr.DestroyEntity(entity);
                entity = null;
                
            }
			

		}
		Vehicule.LandVehicule _lv;
        LightTower mLightTower;
        Queue _waypoints;
		Behaviour _behaviour;
		int _wpcounter;
        //ObjectTextDisplay mObjectTextDisplay;
        
		
		#endregion
		
		
		#region Properties
        public LightTower lightTower
        {
            get { return mLightTower; }
        }
		public Behaviour CurrentBehaviour
		{
			get {return _behaviour;}
			set {_behaviour=value;}
		}
		
  
        public override Vector3 Position
        {
            get { return _node.Position; }
            set
            {
                _node.Position = value;
                _lv.Position = value;
            }
        }
        public override Quaternion Orientation
        {
            get { return _node.Orientation; }
            set
            {
                _node.Orientation = value;
                _lv.Orientation = value;
            }
        }
        
		public Mogre.Vector3 CurrentTarget
        {
            get { return (Vector3)(((T_WayPoint)_waypoints.Peek()).pos); }
            
        }

		#endregion
		
		
		#region Constructor
		
		public MobileActor(World iWorld, String iName,Vector3 iPos,Quaternion iOrient, String iMeshName, bool hasRadar)
            :base(iWorld,iName,iPos,iOrient)
		{
			_lv = new Vehicule.LandVehicule();
			_waypoints=new Queue(8);
			_wpcounter=0;


            //necessary to repeat as Properties overloaded
            Position = iPos;
            Orientation = iOrient;

            try
            {
                if (_entity!=null)
                    _world.MainWindow.SceneMgr.DestroyEntity(_entity);
                _entity = _world.MainWindow.SceneMgr.CreateEntity(iName, iMeshName);
                
            }
            catch (Exception e)
            {
                iWorld.MainWindow.logWarning("Error creating entity for " + iName);
                iWorld.MainWindow.log(e.Message);

            }
            //_node.SetScale(1f, 10f, 10f);

            mNodeEntity.AttachObject(_entity);
            mNodeEntity.SetScale(5f, 5f, 5f);

            
            _behaviour=Behaviour.Idle;
		
            
            _lv.setSpeed(25.0f);
            _lv.setMaxSpeed(100.0f);
            _lv.setMaxForce(20.0f);
            _lv.BankingBehaviour = true;
            _lv.setMass(0.3f);
            _lv.Orientation=_node.Orientation;

            _entity.QueryFlags=(uint)(World.QueryFlags.SELECTABLE_MASK| World.QueryFlags.HUMAN_MASK);

            mLightTower = new LightTower(this, iName + "_LT",300.0f, 10.0f);
        }
		#endregion

        public override void Dispose()
        {
            mDead=true;
            clearPath();
            mLightTower.Dispose();
            base.Dispose();
            
            

        }

        public override bool DoMouseDown(Vector3 loc, MOIS.MouseButtonID iButton, bool iShift, bool iCtrl, bool iAlt, MyOgreWindow.QueryResult iQr, ISelectable iMO)
           {
            switch (iButton)
            {
                case MOIS.MouseButtonID.MB_Right:
                    _world.MainWindow.log("Treated MouseDown message : MB_RIGHT"); 
                    if (iCtrl)
                    {
                        mLightTower.setDirAmpl(loc);
                        return true;

                    }
                    if (iQr==MyOgreWindow.QueryResult.Ground)
                    {
                        loc.y += 10;
                        addWayPoint(loc, !iShift,iAlt);
                        _world.MainWindow.log("Treated MouseDown message : MB_RIGHT"); 
                        return true;
                    }

                    break;
               

            }
            return false;
        }

        public override bool DoMouseUp(Vector3 loc, MOIS.MouseButtonID iButton, bool iShift, bool iCtrl, bool iAlt, MyOgreWindow.QueryResult iQr, ISelectable iMO)
        {
            switch (iButton)
            {
                case MOIS.MouseButtonID.MB_Right:
                    _world.MainWindow.log("Treated MouseDown message : MB_RIGHT");
                    if (iCtrl)
                    {
                        mLightTower.setDirAmpl(loc);
                        return true;

                    }
                    if (iQr == MyOgreWindow.QueryResult.Ground)
                    {
                        loc.y += 10;
                        addWayPoint(loc, !iShift, iAlt);
                        _world.MainWindow.log("Treated MouseDown message : MB_RIGHT");
                        return true;
                    }

                    break;


            }
            return false;
        }

        public override bool timerUpdate(float iTimeInterval)
        {
            if (mDead)
            {
                _world.MainWindow.logWarning("DeadManWalking!");
                return true;
            }
            bool returnValue = base.timerUpdate(iTimeInterval);
            moveOnGround(iTimeInterval);

            return returnValue;
        }

        protected void _setNodeOrientation()
        {
            Quaternion q = new Quaternion(convertVectorToMOGRE(_lv.forward()),
                                        convertVectorToMOGRE(_lv.up()),
                                        convertVectorToMOGRE(_lv.side()));
            _node.Orientation = q;

        }
        #region Movement methods
        
        public void moveOnGround(float iTimeInterval)
        {
            
            float alt = _node.Position.y;
            moveFree(iTimeInterval);
			Vector3 norm=Vector3.UNIT_Y;
			//setAltitude(_world.MainWindow.checkTerrainHeight(_node.Position, out norm));
            setAltitude(alt);
			forceNormal(norm);
            if (mLightTower != null) mLightTower.doTimer(iTimeInterval);
        }
		
		/** Move without constriaint, typical for flying objects...
		*/
		public void moveFree(float timeInterval)
        {
            if (_behaviour==Behaviour.FollowPath)
			{
				if (_waypoints.Count>0)
				{
					Vector3 dest=(Vector3) ((T_WayPoint)_waypoints.Peek()).pos;
					_lv.move(timeInterval,dest);
					Vector3 dist=dest-_lv.Position;
					if (dist.Length<_CONTACT_DISTANCE)
					{
						T_WayPoint wp=(T_WayPoint)_waypoints.Dequeue();
                        wp.Dispose();
						if (_waypoints.Count==0)
							_behaviour=Behaviour.Idle;
					}
				}
			}
			if (_behaviour==Behaviour.Idle)
			{
				_lv.slowDown(timeInterval);
			}

            try
            {
                _node.Position = _lv.Position;
                _node.Orientation = _lv.Orientation;
            }
            catch (Exception e)
            {
            }
			

        }
		#endregion
        public void clearPath()
        {
            T_WayPoint wp;
        
            while (_waypoints.Count > 0)
            {
                wp = (T_WayPoint)_waypoints.Dequeue();
                wp.Dispose();

            }
            _behaviour = Behaviour.Idle;
        }

		public void addWayPoint(Vector3 iWP, bool clear, bool useAStar)
		{
			if ((_behaviour==Behaviour.FollowPath)||((_behaviour==Behaviour.Idle)))
			{
				T_WayPoint wp;
                if (clear) clearPath();
                _behaviour=Behaviour.FollowPath;
                
                // Check if is using a path finding algorythm
                if (useAStar)
                {
                    List<Vector3> lwp=_world.compute2DPath(_node.Position,iWP,_world.MyMap);
                    /// if no path can be found
                    if (lwp==null)
                    {
                        _behaviour=Behaviour.Idle;
                
                    }else
                    {
                        
                        _behaviour = Behaviour.FollowPath;
                        foreach (Vector3 v in lwp)
                        {
                           wp=new T_WayPoint(_world,v,_name+"_wp#"+(_wpcounter++));
				            _waypoints.Enqueue(wp);

                        }
                    }
                }
                else // if not using path finding, just add the point.
                {
				    wp=new T_WayPoint(_world,iWP,_name+"_wp#"+(_wpcounter++));
				    _waypoints.Enqueue(wp);
				
                }
                
			}
		}
       
        public void setAltitude(float alt)
        {
            Position = new Vector3(Position.x, alt, Position.z);
        }
        public void forceNormal(Vector3 n)
        {
            _lv.ForcedNormal = n;
        }
        
        // Utility classes

        OpenSteerDotNet.Vector3 convertVectorToOS(Mogre.Vector3 v)
        {
            return new OpenSteerDotNet.Vector3(v.x, v.y, v.z);
        }
        Mogre.Vector3 convertVectorToMOGRE(OpenSteerDotNet.Vector3 v)
        {
            return new Mogre.Vector3(v.x, v.y, v.z);
        }


        public void testVisibility(MobileActor ma)
        {

            _world.MainWindow.log(" Beginning visibility test");
            _world.MainWindow.log("  Range = "+mLightTower.Range);
            
            _world.MainWindow.log("  Distance = " + this.FlatDistanceTo(ma));

            
            
            if (_world.MainWindow.VisibleQuery(this.Position, ma, (uint)(World.QueryFlags.HUMAN_MASK|World.QueryFlags.SELECTABLE_MASK)
                , mLightTower.Range,
                mLightTower.Node.WorldOrientation * Vector3.UNIT_X, mLightTower.FOV))
                _world.MainWindow.log(" Object " + ma.Name + " in sight");

        }



    }

    namespace Vehicule
    {
        class LandVehicule : OpenSteerDotNet.SimpleVehicle
        {
            Vector3 _forcedNormal;
            const float _brakingRate = 0.5f;

            public LandVehicule()
            {
                _forcedNormal = Vector3.UNIT_Y;
            }
            public new Vector3 Position
            {
                get { return convertVectorToMOGRE(base.Position); }
                set { setPosition(convertVectorToOS(value)); }
            }

            public Quaternion Orientation
            {
                set
                {
                    setForward(convertVectorToOS(value.XAxis));
                    setUp(convertVectorToOS(value.YAxis));
                    setSide(convertVectorToOS(value.ZAxis));
                }
                get
                {
                    return new Quaternion(convertVectorToMOGRE(forward()),
                                        convertVectorToMOGRE(up()),
                                        convertVectorToMOGRE(side()));

                }

            }
            public Vector3 ForcedNormal
            {
                get { return _forcedNormal; }
                set
                {
                    _forcedNormal = (ForcedNormal.Length != 1.0f) ? value.NormalisedCopy : value;
                }

            }
            public void move(float dt, Vector3 target)
            {
                if (target == Vector3.ZERO)
                    applySteeringForce(steerForWander(dt), dt);
                else
                    applySteeringForce(steerForSeek(convertVectorToOS(target)), dt);

                forceNormalUp();
            }
            public void slowDown(float dt)
            {
                if (speed() == 0.0f) return;
                if (speed() < 0.1f)
                    setSpeed(0.0f);
                else
                    setSpeed(speed() * (1 - maxForce() * mass() * dt / 100));

                forceNormalUp();
            }


            void forceNormalUp()
            {
                setUp(convertVectorToOS(_forcedNormal));
                setForward(OpenSteerDotNet.OpenSteerUtility.perpendicularComponent(this.forward(), convertVectorToOS(_forcedNormal)));
                setSide(localRotateForwardToSide(forward()));
            }

            // Utility methods

            OpenSteerDotNet.Vector3 convertVectorToOS(Mogre.Vector3 v)
            {
                return new OpenSteerDotNet.Vector3(v.x, v.y, v.z);
            }
            Mogre.Vector3 convertVectorToMOGRE(OpenSteerDotNet.Vector3 v)
            {
                return new Mogre.Vector3(v.x, v.y, v.z);
            }


        }


    }

	/** Interface for objects that can suffer damage
     */
	public interface IDestroyable
	{
        void SetHPMax(int iHPMax);

        int RemainingHP();

        float DamageLevel();

        void Inflict(int iDamage);

        bool Repair(int iAmount);

        
	}
    public interface ITimerUpdatable
    {
        bool timerUpdate(float iTimeInterval);
    }

    public interface IMyMouseListener
    {
        bool DoMouseUp(Vector3 iPos, MOIS.MouseButtonID button, bool isShift, bool isCtrl, bool isAlt, MyOgreWindow.QueryResult iQr, ISelectable iMO);
        bool DoMouseDown(Vector3 iPos, MOIS.MouseButtonID button, bool isShift, bool isCtrl, bool isAlt, MyOgreWindow.QueryResult iQr, ISelectable iMO);

    }

    public interface ISelectable
    {
        bool IsSelected();
        void SetSelected(bool iSel);
    }
    /*
	public interface IRadio
	{
		ListArray <IRadio> mNeighbours;
		int mNumBaseConnections;
		
		//public bool ConnectedToBase(get{};);
		//public int NumNeighbours(get{});
		
		public bool connectTo(IRadio other);
		public void connectFrom(IRadio other);
		public void unconnectTo(IRadio other);
		public void unconnectFrom(IRadio other);
		
		public void unconnectAll();
	
	}
	
	public interface IRadar
	{
		protected MO_Radar mRadar;
		public float DistanceInDirection(int iAngle);
		
	
	}
	*/
}
